﻿using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;

namespace Zo.Queues
{
    /// <summary>
    /// 单线程安全队列基类。
    /// </summary>
    /// <remarks>队列集合节点处理。</remarks>
    /// <typeparam name="T"></typeparam>
    public abstract class MonoConcurrentQueueBase<T> : IProducerConsumerCollection<T>, IEnumerable<T>, ICollection, IEnumerable
    {
        /// <summary>
        /// 队列元素总数。
        /// </summary>
        protected int _count;

        /// <summary>
        /// 首节点。
        /// </summary>
        protected Node _head = new Node();

        /// <summary>
        /// 末节点。
        /// </summary>
        protected Node _tail;

        protected MonoConcurrentQueueBase()
        {
            _tail = _head;
        }

        protected MonoConcurrentQueueBase(IEnumerable<T> collection)
            : this()
        {
            foreach (T item in collection)
                Enqueue(item);
        }

        public bool IsEmpty => Count == 0;

        public int Count => _count;

        public object SyncRoot { get; } = new object();

        public bool IsSynchronized { get; } = true;

        public IEnumerator<T> GetEnumerator()
        {
            return InternalGetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return InternalGetEnumerator();
        }

        public void CopyTo(Array array, int index)
        {
            if (array == null)
                throw new ArgumentNullException(nameof(array));

            if (array.Rank > 1)
                throw new ArgumentException("数组不可以是多维的。");

            if (array.GetLowerBound(0) != 0)
                throw new ArgumentException("数组的元素索引必须是从 0 开始。");

            var dest = array as T[];

            if (dest == null)
                throw new ArgumentException("当前数组无法转换成指定类型的数组。", nameof(array));

            CopyTo(dest, index);
        }

        public void CopyTo(T[] array, int index)
        {
            if (array == null)
                throw new ArgumentNullException(nameof(array));
            if (index < 0)
                throw new ArgumentOutOfRangeException(nameof(index));
            if (index >= array.Length)
                throw new ArgumentException("索引不可以大于或者等于数组长度。", nameof(index));

            var e = InternalGetEnumerator();
            var i = index;
            while (e.MoveNext())
            {
                if (i == array.Length - index)
                    throw new ArgumentException(
                        "索引超出数组上限。", nameof(array));

                array[i++] = e.Current;
            }
        }

        public bool TryAdd(T item)
        {
            Enqueue(item);
            return true;
        }

        public bool TryTake(out T result)
        {
            result = default(T);
            var update = true;

            while (update)
            {
                var oldHead = _head;
                var oldNext = oldHead.Next;

                if (oldNext == null)
                {
                    result = default(T);
                    return false;
                }

                result = oldNext.Value;

                //check if head has been updated
                update = _head != oldHead;
            }
            return true;
        }

        public T[] ToArray() => new List<T>(this).ToArray();

        private IEnumerator<T> InternalGetEnumerator()
        {
            var my_head = _head;
            while ((my_head = my_head.Next) != null)
            {
                yield return my_head.Value;
            }
        }

        /// <summary>
        /// 入队。
        /// </summary>
        /// <param name="item"></param>
        public abstract void Enqueue(T item);

        /// <summary>
        /// 出队。
        /// </summary>
        /// <returns>元素。</returns>
        public abstract T Dequeue();

        /// <summary>
        /// 出队。
        /// </summary>
        /// <param name="result">元素。</param>
        /// <returns>是否成功。</returns>
        public abstract bool TryDequeue(out T result);

        /// <summary>
        /// 节点。
        /// </summary>
        protected class Node
        {
            /// <summary>
            /// 下一个元素。
            /// </summary>
            public Node Next;

            /// <summary>
            /// 当前元素。
            /// </summary>
            public T Value;
        }
    }
}